/**
 * 农历（阴历）万年历
 * LunarCalendar
 */

const lunarInfo = [
  0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
  0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
  0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
  0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
  0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
  0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0,
  0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
  0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
  0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
  0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
  0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
  0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
  0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
  0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
  0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0
];

// 天干
const Gan = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];

// 地支
const Zhi = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];

// 生肖
const Animals = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'];

// 中文数字
const chineseNumber = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'];

// 农历月份
const lunarMonths = ['正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月'];

// 农历日期
const lunarDays = [
  '初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十',
  '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
  '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'
];

/**
 * 公历转农历函数
 * @param {Number} year 公历年
 * @param {Number} month 公历月
 * @param {Number} day 公历日
 * @return {Object} 农历日期对象
 */
function solar2lunar(year, month, day) {
  // 参数区间1900.1.31~2100.12.31
  if (year < 1900 || year > 2100) {
    return -1;
  }
  
  // 公历传参：年月日
  if (year === 1900 && month === 1 && day < 31) {
    return -1;
  }
  
  // 未传参 获得当天
  let objDate;
  if (!year) {
    objDate = new Date();
  } else {
    objDate = new Date(year, parseInt(month) - 1, day);
  }
  
  let i, leap = 0, temp = 0;
  // 修正ymd参数
  const y = objDate.getFullYear();
  const m = objDate.getMonth() + 1;
  const d = objDate.getDate();
  
  // 计算距离1900.1.31的天数
  let offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000;
  
  // 计算农历年
  for (i = 1900; i < 2101 && offset > 0; i++) {
    temp = getLunarYearDays(i);
    offset -= temp;
  }
  
  if (offset < 0) {
    offset += temp;
    i--;
  }
  
  // 农历年
  const lunarYear = i;
  
  // 计算闰月
  leap = getLeapMonth(lunarYear);
  let isLeap = false;
  
  // 计算农历月
  for (i = 1; i < 13 && offset > 0; i++) {
    // 闰月
    if (leap > 0 && i === (leap + 1) && isLeap === false) {
      --i;
      isLeap = true;
      temp = getLeapDays(lunarYear);
    } else {
      temp = getLunarMonthDays(lunarYear, i);
    }
    
    // 解除闰月
    if (isLeap === true && i === (leap + 1)) {
      isLeap = false;
    }
    
    offset -= temp;
  }
  
  // 闰月导致数组下标重叠取反
  if (offset === 0 && leap > 0 && i === leap + 1) {
    if (isLeap) {
      isLeap = false;
    } else {
      isLeap = true;
      --i;
    }
  }
  
  if (offset < 0) {
    offset += temp;
    --i;
  }
  
  // 农历月
  const lunarMonth = i;
  
  // 农历日
  const lunarDay = offset + 1;
  
  // 获取干支历
  const gzYear = getGanZhiYear(lunarYear);
  const gzMonth = getGanZhiMonth(lunarYear, lunarMonth);
  const gzDay = getGanZhiDay(y, m, d);
  
  return {
    'lYear': lunarYear,
    'lMonth': lunarMonth,
    'lDay': lunarDay,
    'Animal': getAnimal(lunarYear),
    'IMonthCn': (isLeap ? '闰' : '') + lunarMonths[lunarMonth - 1],
    'IDayCn': lunarDays[lunarDay - 1],
    'gzYear': gzYear,
    'gzMonth': gzMonth,
    'gzDay': gzDay,
    'isLeap': isLeap
  };
}

/**
 * 获取农历年总天数
 * @param {Number} year 农历年
 * @return {Number}
 */
function getLunarYearDays(year) {
  let i, sum = 348;
  for (i = 0x8000; i > 0x8; i >>= 1) {
    sum += (lunarInfo[year - 1900] & i) ? 1 : 0;
  }
  return (sum + getLeapDays(year));
}

/**
 * 获取农历年闰月天数
 * @param {Number} year 农历年
 * @return {Number}
 */
function getLeapDays(year) {
  if (getLeapMonth(year)) {
    return ((lunarInfo[year - 1900] & 0x10000) ? 30 : 29);
  }
  return 0;
}

/**
 * 获取农历年闰月月份
 * @param {Number} year 农历年
 * @return {Number} 闰月的月份，0表示没有闰月
 */
function getLeapMonth(year) {
  return (lunarInfo[year - 1900] & 0xf);
}

/**
 * 获取农历年某月的总天数
 * @param {Number} year 农历年
 * @param {Number} month 农历月
 * @return {Number}
 */
function getLunarMonthDays(year, month) {
  return ((lunarInfo[year - 1900] & (0x10000 >> month)) ? 30 : 29);
}

/**
 * 获取生肖
 * @param {Number} year 农历年
 * @return {String}
 */
function getAnimal(year) {
  return Animals[(year - 4) % 12];
}

/**
 * 获取干支纪年
 * @param {Number} year 农历年
 * @return {String}
 */
function getGanZhiYear(year) {
  const ganKey = (year - 3) % 10;
  const zhiKey = (year - 3) % 12;
  return Gan[ganKey] + Zhi[zhiKey];
}

/**
 * 获取干支纪月
 * @param {Number} year 农历年
 * @param {Number} month 农历月
 * @return {String}
 */
function getGanZhiMonth(year, month) {
  const ganKey = (year - 1900) * 12 + month + 11;
  return Gan[ganKey % 10] + Zhi[ganKey % 12];
}

/**
 * 获取干支纪日
 * @param {Number} year 公历年
 * @param {Number} month 公历月
 * @param {Number} day 公历日
 * @return {String}
 */
function getGanZhiDay(year, month, day) {
  const dateObj = new Date(year, month - 1, day);
  const num = dateObj.getTime() / 86400000 + 25567 + 10;
  return Gan[num % 10] + Zhi[num % 12];
}

module.exports = {
  solar2lunar: solar2lunar
}; 